In diesem Kapitel werden die Themen InMemory-Datenbanken, NoSQL, XML und JSON behandelt. Wir beginnen mit den InMemory-Datenbanken. Neben den herkömmlichen Datenbanken, die Festplatten zur Speicherung der Daten benutzen, gibt es InMemory-Datenbanken, die ihre Daten komplett im Arbeitsspeicher ablegen. Da stellt sich die Frage, ob deren Daten verloren gehen, wenn das System abgeschaltet wird oder ausfällt. Außerdem ist Arbeitsspeicher deutlich teurer als Festplattenspeicher, weshalb dieser in seiner Größe und damit auch die Datenmenge stark beschränkt ist. Zunächst einmal die Vorteile: InMemory-Datenbanken haben deutlich geringere Zugriffszeiten als herkömmliche, nicht-volatile Datenbankmanagementsysteme. Grob gesagt dauert ein einzelner Zugriff auf Daten im RAM circa einhundert Nanosekunden, der einer SSD circa einhundert Mikrosekunden. Damit ist der Zugriff auf Daten einer SSD ungefähr eintausend Mal langsamer. Noch stärker fällt der Unterschied zu einer Festplatte aus. Dort dauert ein Zugriff circa zehn Millisekunden, damit ist ein einzelner Zugriff auf eine herkömmliche Festplatte circa einhunderttausend Mal langsamer als bei einem Zugriff auf Daten im Arbeitsspeicher. Außerdem sind die Zugriffszeiten wesentlich besser vorhersagbar, da es keine Cache-Probleme durch vergleichsweise langsame Festplattenzugriffe gibt. Aus diesen Gründen werden InMemory-Datenbanken zum Beispiel im Finanzhandel beim High-Frequency Trading, im Bereich des autonomen Fahrens oder Business Intelligence eingesetzt. Typische InMemory-Datenbanken sind kdb+ für High-Frequency Trading, Redis für autonomes Fahren und SAP-HANA im Bereich Business Intelligence. Bei geplanten Vorgängen, also zum Beispiel dem Neustart des Systems, oder ungeplanten Ereignissen wie einem Stromausfall oder Systemabsturz gehen nicht gespeicherte Daten im Arbeitsspeicher verloren. Damit dies nicht geschieht und man eine Form von Persistenz der Daten erreicht, gibt es verschiedene Ansätze. Einige DBMS legen in Intervallen Schnappschuss-Dateien an. Das bedeutet, dass der gesamte Datenbankinhalt zu bestimmten Zeitpunkten in irgendeiner Form auf die Festplatte geschrieben wird. Um nicht ständig den gesamten Datenbestand mittels Schnappschuss-Dateien speichern zu müssen, nutzen einige DBMS zusätzlich Log-Dateien. In diesen Log-Dateien werden nur die Änderungen zwischen zwei Schnappschüssen gespeichert, die sogenannten Deltas. So wird weniger Speicher verbraucht und häufigere Speicherzyklen sind möglich. Man könnte auch eine Kombination von flüchtigem und nicht-flüchtigem Speicher umsetzen. Damit ist zum Beispiel die Kombination von RAM und typischerweise einer SSD gemeint, also die Verbindung von sehr schnellem Arbeitsspeicher mit dem Preisvorteil eines Festplattenspeichers. Wenn man hochverfügbare Datenbanken benötigt, kann man den Dateninhalt auch auf verschiedenen Maschinen spiegeln. Wenn diese immer synchron gehalten werden, kann man sogar dynamisch zur Laufzeit auf herkömmliche Datenbanken wechseln. Häufig kann man auch herkömmliche Datenbanken als reine InMemory-Datenbank oder Hybridlösung umkonfigurieren. Die Wahl der InMemory-Datenbank hängt stark von den Anforderungen an Skalierbarkeit, Persistenz und natürlich auch Plattformunterstützung ab. H-SQL-DB schauen wir uns gleich noch etwas genauer an. H-SQL-DB ist zunächst einmal ein herkömmliches relationales Datenbanksystem. Manchmal spricht man auch nur von H-SQL, damit ist aber immer H-SQL-DB gemeint. H-SQL-DB gilt deshalb als lightweight, weil es kaum Ressourcen benötigt, eine kleine Codebasis hat, oft embedded läuft und auch die Installation und Konfiguration sehr einfach und minimal ist. Im Server-Modus läuft ein separater HSQL-Server, der SQL-Anfragen von Clients über JDBC empfängt. Im Embedded-Modus läuft die Datenbank innerhalb einer Anwendung, das heißt sie ist direkt in das Programm integriert. So kann also die gesamte Datenbank, mit allem was dazu gehört, komplett Teil eines Java-Projektes werden. H-SQL ist ähnlich schnell wie MySQL und wird in OpenOffice Base sowie dessen Ableger LibreOffice Base verwendet und ist damit ähnlich zu Microsoft Access. Das Java-Archiv umfasst alles, was man braucht. Unter anderem beinhaltet sie die Datenbank-Engine, den Server oder den JDBC-Treiber und ist dennoch nur ungefähr ein Megabyte groß. Man kann HSQL auch ohne Server betreiben und damit in eine Java-Anwendung integrieren. Die Kommunikation verläuft dann ausschließlich über API-Aufrufe und wird Teil der Anwendung. Wenn man beispielsweise eine lokale Anwendung hat, die Daten speichert, wäre dieser eingebettete Weg die bessere Alternative als der Zugriff auf Dateien. Im "Memory only"-Modus werden alle Daten nur im Arbeitsspeicher abgelegt und gehen beim Beenden des Servers verloren. Um Datenverlust zu vermeiden, kann man auch Log-Dateien verwenden. Dabei wird jede Änderung, die durch Create, Alter, Insert, Update und Delete durchgeführt wurde, in einer großen SQL-Datei gespeichert. Diese Logdatei hat meist die Endung .script. Beim Neustart wird sie dann geladen und so die Datenbank wiederhergestellt. Man kann HSQL so konfigurieren, dass diese Logdatei automatisch angelegt und beim Neustart direkt eingelesen wird; das ist die Standardmethode, um im Memory-Modus eine Persistenz der Daten zu erhalten. Im nächsten Kapitel schauen wir an, was hinter dem Begriff "NoSQL" steckt. NoSQL steht für "Not only SQL" und nicht für "No-SQL", also kein SQL. NoSQL-Datenbanken haben einen anderen Ansatz als relationale Datenbanken, die ihre Daten in Tabellen speichern. Bei NoSQL-Datenbanken gibt es keine festen Tabellenschemata. Sie werden oft im akademischen Umfeld als strukturierte Datenspeicher, im Bereich Big Data und Echtzeitanalysen oder bei verteilten Systemen verwendet. Es gibt unter anderem dokumentenorientierte Datenbanken, die ihre Daten als JSON, BSON oder XML-Dokumente speichern. Als Beispiele dienen MongoDB, CouchDB oder Firebase Firestore. Graphendatenbanken wie Neo4j, OrientDB oder Amazon Neptune speichern ihre Daten als Knoten und Kanten ab. Die Datenstruktur ist ein gerichteter oder ungerichteter Graph und besonders gut geeignet für hochgradig vernetzte Daten. Key-Value-Datenbanken erinnern an eine Map in Java. Jeder Schlüssel ist eindeutig und verweist auf einen Wert, der beliebige Daten sein kann, zum Beispiel Text, Zahlen, Objekte. Man nutzt sie hauptsächlich für Caching, Session-Management oder einfache Datenhaltung ohne komplexe Abfragen. Beispiele für bekannte Key-Value-Datenbanken sind: Redis, Amazon DynamoDB oder Memcached. Reine Objektdatenbanken wie ObjectDB, db4o, ZODB speichern Daten direkt als Objekte, genau wie in objektorientierten Programmiersprachen. Objektdatenbanken speichern Klassen, Attribute und Beziehungen unverändert ab. Hier werden die eben genannten Ansätze nochmals grafisch gegenübergestellt. Einerseits gibt es die SQL-basierten Datenbanken. Bei diesen machen relationale Datenbanken schätzungsweise achtzig bis neunzig Prozent aus. Die übrigen zehn bis zwanzig Prozent sind OLAP-Datenbanken, die beispielsweise im Bereich Business Intelligence und Data Warehouse eingesetzt werden. Dem gegenüber stehen die NoSQL-Datenbanken, wobei Column-Family-Datenbanken noch nicht erläutert wurden. Column-Family-Datenbanken speichern Daten in Tabellen, aber viel flexibler als klassische relationale Datenbanken. Statt fester Spalten gibt es sogenannte Spaltenfamilien, in denen jede Zeile ganz unterschiedliche Spalten haben kann. Man nutzt sie in verteilten Systemen, bei denen es auf hohe Geschwindigkeit und Skalierbarkeit ankommt wie zum Beispiel bei Cassandra oder HBase. Das nächste Thema ist XML als Möglichkeit zur Verwaltung von Daten. Die Extensible Markup Language speichert Daten in einer hierarchischen Struktur ab. Das Ergebnis sind Textdaten, die von Menschen lesbar und damit gut verständlich sind. Alle Daten, die sich baumförmig darstellen lassen, kann man gut in XML darstellen. XML ist sehr weit verbreitet für den Import und Export von Daten zwischen verschiedenen Computersystemen. Da XML eine Metasprache ist, kann man damit eigene Datenformate schaffen. Im Übrigen basieren viele bekannte Standards auf XML, dazu gehören XHTML, RSS oder SVG. Was heißt eigentlich wohlgeformtes XML? Ein wohlgeformtes XML-Dokument hält alle grundlegenden Syntaxregeln strikt ein und ist dadurch wohlgeformt. Ein XML-Dokument hat genau ein Wurzelelement, das ist das äußerste Element wie der html-Tag in XHTML. Es besitzt Beginn- und Endauszeichner mit Inhalt dazwischen. Der Inhalt kann übrigens auch leer sein. Angenommen, man hat in einer Kundendatenbank ein Feld für Telefonnummer und Faxnummer, der Kunde verfügt aber über keine Faxnummer. Dann bleibt der Inhalt zwischen dem Beginn- und Endtag leer. Man kann in solchen Fällen mit der Kurzschreibweise den Beginn- und Endtag zusammenfassen. Beginn- und Endauszeichner müssen ähnlich wie eine Zwiebel paarweise auf derselben Ebene angeordnet sein. Jedes Element muss vollständig geschlossen sein, bevor das übergeordnete Element endet oder ein neues Element auf derselben Ebene beginnt. Außerdem darf ein Element nicht zweimal dasselbe Attribut haben. Stünde hier table border="1" gefolgt von table border="2" im selben Element, wäre das nicht erlaubt. Zur XML gehört das Document Object Model. Das DOM ist eine Programmierschnittstelle, die XML- oder HTML-Dokumente als Baumstruktur darstellt. Weil jedes Element ein eigenes Objekt ist, kann man gezielt auf einzelne Teile des Dokumentes zugreifen. Außerdem ist das DOM plattform- und programmiersprachenunabhängig, daher kann man sehr leicht Dokumente von verschiedenen Systemen portieren. Eine DOM-konforme Umsetzung besteht aus Klassen, deren Attributen und Methoden, mit denen man die Struktur eines Dokuments lesen und ändern kann. Für die Standardisierung des DOM ist das World Wide Web Consortium zuständig. Daten, die baumförmig organisierbar sind, kann man mit einer XML-Datenbank verwalten. Analog zu SQL in relationalen Datenbanken benutzt man in XML-Datenbanken XPATH, XQuery und XQuery Update für die Datenabfrage und Datenmanipulation. XPATH steht für XML Path Language und dient dazu, bestimmte Teile, also Knoten, auszuwählen und sich durch ein XML-Dokument zu bewegen. Es ähnelt "WHERE" in SQL, kann aber keine Daten verändern. XQuery ist mächtiger und kann nicht nur Knoten auswählen, sondern auch neu zu ordnen. Es ist eher mit einer vollständigen SQL-Abfrage vergleichbar. XQuery Update ist eine Erweiterung von XQuery und beherrscht das Einfügen, Löschen oder Verändern von Knoten, also Daten, in einem XML-Dokument. Um XML-Daten darzustellen oder zu exportieren, können sie vorher mit XQuery gut angepasst werden. Für die Umformung wird die Extensible Stylesheet Language, XSL, benutzt. Sie umfasst zwei Komponenten: Die XSL Transformations, XSLT, für HTML, Text oder ein anderes XML-Format und XSL-Formatting Object, XSL-FO. Mit XSL-FO kann man beschreiben, wie beispielsweise ein PDF aus XML-Daten aussehen soll. Die Umwandlung in das PDF aus dieser Vorlage erledigen dann Tools wie Apache FOP. XPATH gilt als Fundament für XSLT und XQuery. Es spielt eine zentrale Rolle bei der Arbeit mit XML und wird vom W3C weiterentwickelt. Hier haben wir ein XML-Dokument. Es ist aus zwei Kapiteln aufgebaut und frei definiert. Da es standalone ist, gibt es auch keine Document Type Definition, die dieses XML näher beschreibt. "Dok" ist die Wurzel des Dokuments, "PA" steht für Paragraph und ist frei gewählt. Man kann jetzt mit XPATH über bestimmte Ausdrücke die gewünschten Elemente ausgeben. XPATH ist eine eigene Sprache, mit einer eigenen Syntax für Pfade und Bedingungen. Der Slash steht beispielsweise für die Wurzel, die eckigen Klammern für Filter und ein doppelter Slash für eine beliebige Tiefensuche. Slash und Asterisk bedeuten folgendes: Der Slash steht wie gesagt für die Wurzel, der Asterisk ist ein Platzhalter für ein beliebiges Element. Zusammen greift es auf das Wurzelelement zu, unabhängig vom Namen. XQuery ist ebenfalls eine eigenständige Sprache, Turing-vollständig, standardisiert und gedacht für den Zugriff auf XML-Dateien. Turing-vollständig deshalb, weil es über alle grundlegenden Kontrollstrukturen und Operationen verfügt, um Dinge zu berechnen wie Schleifen und Wiederholungen, If-Abfragen, Variablen und Funktionen. Die grundlegende Datenstruktur in XQuery ist eine geordnete Liste. Eine Sequenz kann zum Beispiel ein ganzes XML-Dokument sein oder nur ein Teil davon. Dieses XML zeigt eine Liste von Teilen, sie ist unser Quelldokument. Diese Teile haben eine eigene ID, einen Namen und da sie fest zum Ganzen, zum Beispiel "Auto", gehören und allein für sich keinen Sinn machen, bilden sie eine Komposition. Man sieht hier wieder, wie man Daten auch anders als in Form von einer Tabelle organisieren kann. Das ist eine XQuery-Funktion. Das Skript wandelt rekursiv das Quelldokument mit Teile-Daten in einen Baum mit den einzelnen part-Elementen um. Man erkennt for-Schleifen, return-Anweisungen, die an Java erinnern, außerdem XML-Elemente wie part oder part-tree. Das ist das Zieldokument nach der Umwandlung als Baumstruktur mit sachlich zusammenhängenden Elementen als Teil-Ganzes-Beziehung. Bei XSLT geht es darum, das gesamte Dokument in ein anderes Format zu transformieren. Sie ist eine eigene, Turing-vollständige Programmiersprache und benötigt zwei Dinge für eine Ausgabe. Zum einen den XSLT-Code, zum anderen den gesamten DOM-Baum einer XML-Datei. Zusammen wird dies mit einem XSLT-Prozessor verarbeitet und erzeugt eine vorher festgelegte Ausgabe, zum Beispiel ein neues XML-Dokument, ein HTML-Dokument, ein Text-Dokument oder auch Formate wie JSON. Dies ist eine Übersicht über die verbreiteten XML-Datenbanken. Einerseits gibt es reine XML-Datenbanksysteme, die für XML-Dokumente optimiert wurden. Es gibt auch sogenannte XML-enabled DBS, das sind relationale DBMS, die zusätzlich XML-Dokumente unterstützen. Am weitesten verbreitet sind Oracle Database und Microsoft SQL Server, gefolgt von IBM DB2. Hier sehen wir die Berkeley DB XML Architektur. Man hat eine XML API, wo statt SQL-Befehlen hier nun XML entgegengenommen wird. Intern verarbeitet dieses DBMS XML-Daten als Dokumente, die aus Knoten bestehen, und verwendet Indizes für Suche und Zugriff; diese werden in einem Container abgelegt. XQuery wird durch einen eigenen Optimizer analysiert und auf den Container ausgeführt. Für die Datenverwaltung nutzt Berkeley DB XML einen Page Cache für die häufig verwendeten Datenseiten und Protokolldateien für die Änderungen an der Datenbank. Die B-Baumstruktur ist für die effiziente Speicherung und Suche zuständig und bildet die Indizes ab. Im letzten Teil geht es um die JavaScript Object Notation (JSON). JSON ist ein menschlich gut lesbares Datenformat. Es hat eine klare Syntax und keine zusätzlichen Tags wie XML, was es sehr kompakt macht. Durch die enge Bindung an JavaScript soll jedes JSON-Dokument auch gültiges JavaScript sein und durch die eval-JavaScript-Funktion ausgewertet werden können. JSON hängt zwar historisch eng an JavaScript, ist aber unabhängig und wird von allen gängigen Programmiersprachen unterstützt. Es ist generell weniger strukturiert als XML und eignet sich besser für unstrukturierte Daten, die nicht so einfach in eine relationale Datenbank abgebildet werden können. Mit JSON oder XML ist man deutlich flexibler und nicht an das starre E-R-Modell gebunden. Mit Node.js kann man JavaScript nicht nur im Browser, sondern auch serverseitig einsetzen. Damit kann man zum Beispiel Webserver aufsetzen. Node.js wird in der JavaScript-Laufzeitumgebung „V8“ ausgeführt und ist auch deshalb ressourcensparend und effizient im Umgang mit vielen Verbindungen, weil nicht für jede geöffnete Verbindung ein eigener Thread gestartet wird. Das gesamte Node.js läuft in einem einzigen Thread. Hier kann man Node.js herunterladen. Es geht sehr schnell, einen solchen Server in Betrieb zu nehmen. Das ist ein Beispiel für ein einfaches Schachspiel in JavaScript. Das Schachbrett wird in einem zweidimensionalen Array mit JavaScript-Objekten dargestellt. Der rot umrandete Eintrag bedeutet, dass ein neuer Springer erstellt wird. Der Boolean-Wert true bezieht sich auf die Farbe der Figur. True bedeutet weiß, false bedeutet schwarz. JavaScript-Objekte sind generell wesentlich leichtgewichtiger als Java-Objekte. Die Datenkapselung existiert nicht in dieser Weise. JavaScript ist untypisiert und nicht objektorientiert. Das heißt, die Datenkapselung und die Variablenbezeichnung werden lockerer gehandhabt. Mit der Funktion "F" können neue JavaScript-Objekte angelegt werden. In getFigur wird der aus Gründen der Handhabbarkeit neu indiziert, damit bei eins, eins angefangen wird zu zählen und nicht bei sieben, null. Man kann nun direkt in JavaScript die Daten auslesen, also welcher Figurentyp vorliegt und welche Farbe die Figur hat. Hier wird nun eine LowDB-Datenbank verwendet, um den aktuellen Zustand des Schachspiels in einer JSON-Datei abzuspeichern. LowDB ist eine kleine, leichtgewichtige JSON-Datenbank für JavaScript-Anwendungen. Zuerst wird die alte Datei mit dem alten Spielstand gelöscht, dann wird ein LowDB-Objekt angelegt. In dieser werden erst die globalen Parameter, also wer am Zug ist, wie viele Züge es insgesamt schon gab und weitere, als Key-Value-Paare gespeichert. Dann läuft man das Brett ab und speichert überall, wo eine Figur steht, die Figur und ihre Koordinaten ab. In der gespeicherten JSON-Datei sieht man anhand von anzZuege = 0, dass das Spiel noch nicht gestartet wurde. Auf Position 1,8, was im Ausschnitt a8 entspricht, ist ein Turm. Dieser ist nicht weiß und wurde auch noch nicht bewegt. Auf Position 2,8 steht der Springer. LowDB ist erst einmal nur eine JSON-Datei und kein vollständiges DBMS. Man kann aber mit einem richtigen DBMS auf JSON-Daten zugreifen und diese verarbeiten. Der Vorteil von LowDB ist der wesentlich geringere Overhead. Zum Beispiel fehlen die Start- und End-Tags wie bei XML. Der Nachteil ist, dass die Daten im Vergleich relativ unstrukturiert sind und weniger Beschreibungen der Daten vorliegen. Wenn Sie sich für NoSQL-Datenbanken interessieren, können Sie aufbauend auf dieser Veranstaltung „Datenmanagement“ ein passendes Wahlfach in den höheren Semestern dazu wählen.